'''图片上传管理器类UploadManager.

对外使用的主要类是UploadManager,基本上只有一个upload方法,用于上传文件到指定
域名.成功则返回{'name', 'tags', 'url', 'data'},失败返回None.
其中:
'name'是图片的文件名;
'tags'是一个字符串表示图片标签,所有标签以','结束方便搜索;
'url'是图片的地址;
'data'则是上传资料记录字符串,一般记录上传的返回,不想记录可以留空

抽象类Uploader是上传器类,规定上传的基本行为.继承该类的子类应该具体定义
_upload方法,即具体如何上传,返回什么.

子类:
imgurl_org_Uploader,匿名图床imgurl.org的上传,此图床效果不错;
sm_ms_Uploader,匿名图床sm.ms的上传,这图床的图片显示效果不好;
upload_cc_Uploader,匿名图床upload.cc的上传,此图床效果不错;

测试后,发现多数不需要data,浏览器的参数多是post的files的参数,注意files字典
规定的key,不同图床不同.在浏览器f12的参数中看.

最终更新日期:
2019/7/10
'''

import os
import sys
import json
import urllib
import abc

import requests

import log

## 抽象类Uploader,比较简单,继承时定义domain,指定上传域名,重写_uploadFile方法
class Uploader(metaclass = abc.ABCMeta):

  ## 创建时记录域名,作为标识符
  def __init__(self, domain):
    self._domain = domain
    self._log = log.Log(os.path.join(sys.path[0], r'upload_log.txt'))
    return
    
  ## 文件上传.成功返回{'url', 'data'},失败返回None
  @abc.abstractmethod
  def _uploadFile(self, filepath):
    pass
    
  ## 上传,指定文件路径,以及分类标签组,使用抽象方法_uploadFile,成功则根据
  ## _uploadFile的返回值,返回{'name', 'url', 'tags', 'data'},失败返回None
  def upload(self, filepath, tags = []):
    ## 检查上传文件
    if (not (os.path.exists(filepath))):
      ('Upload failed!Can not find filepath:{}!'.format(filepath))
      return None
    ## 检查tags数组,并生成记录的字符串到tags,注意所有标签不能含有',',变成字符串
    ## 时全部标签以','结束,方便sql语句查找
    for tag in tags:
      if (',' in tag):
        print('Uploadfailed!"," should not in a tag!')
    tags = ','.join(tags)+','
    ## 生成记录的文件名
    name = filepath.split('\\')[-1] 
    ## 上传文件,对返回结果进行判断,编辑
    print('Uploading {0} to {1}'.format(filepath, self._domain))
    result = self._uploadFile(filepath)
    if (result):
      result['name'] = name
      result['tags'] = tags
      print('Upload {} successed!'.format(filepath))
      self._log.reset()
      self._log.add('name: {0}\nurl: {1}'.format(result['name'],result['url']))
      self._log.save()
    else:
      print('Upload {} failed!Something wrong when uploading file!'.format(
        filepath))
    return result
  
## 继承抽象类Uploader的imgurl_org_Uploader类,域名imgurl.org
class imgurl_org_Uploader(Uploader):

  def __init__(self):
    Uploader.__init__(self, 'imgurl.org')
    return
    
  def _uploadFile(self, filepath):
    apiUrl = r'https://imgurl.org/api/upload' # 上传文件url
    filename = urllib.parse.quote(os.path.basename(filepath)) # 处理文件名
    ## 制作上传需要的数据资料
    try:
      file = open(filepath,'rb')
    except:
      print('Can not open {} !'.format(filepath))
      return None
    files = {'file': (filename, file)}
    ## 上传文件开始
    try:
      resp = requests.post(apiUrl, files = files)
    except:
      print('Something wrong when uploading file to {}'.format(self._domain))
      return None
    if (resp.status_code != 200):
      print('Upload status_code != 200')
      return None
    try:
      print('Upload respone is:\n{}'.format(resp.text))
      respJson = json.loads(resp.text)
      url = respJson['url']
    except:
      print('Upload respone is not json or have no "url"!')
      return None
    return {'url': url, 'data': resp.text}  

    
## 继承抽象类Uploader的sm_ms_Uploader类,域名sm.ms
class sm_ms_Uploader(Uploader):

  def __init__(self):
    Uploader.__init__(self, 'sm.ms')
    return
    
  def _uploadFile(self, filepath):
    apiUrl = r'https://sm.ms/api/upload' # 上传文件url
    filename = urllib.parse.quote(os.path.basename(filepath)) # 处理文件名
    ## 制作上传需要的数据资料
    try:
      file = open(filepath,'rb')
    except:
      print('Can not open {} !'.format(filepath))
      return None
    files = {'smfile': (filename, file)}
    ## 上传文件开始
    try:
      resp = requests.post(apiUrl, files = files)
    except:
      print('Something wrong when uploading file to {}'.format(self._domain))
      return None
    if (resp.status_code != 200):
      print('Upload status_code != 200')
      return None
    try:
      print('Upload respone is:\n{}'.format(resp.text))
      respJson = json.loads(resp.text)
      url = respJson['data']['url']
    except:
      print('Upload respone is not json or have no ["data"]["url"]!')
      return None
    return {'url': url, 'data': resp.text}

 
## 继承抽象类Uploader的upload_cc_Uploader类,域名upload.cc
class upload_cc_Uploader(Uploader):

  def __init__(self):
    Uploader.__init__(self, 'upload.cc')
    return
    
  def _uploadFile(self, filepath):
    apiUrl = r'https://upload.cc/image_upload' # 上传文件url
    filename = urllib.parse.quote(os.path.basename(filepath)) # 处理文件名
    ## 制作上传需要的数据资料
    try:
      file = open(filepath,'rb')
    except:
      print('Can not open {} !'.format(filepath))
      return None
    files = {'uploaded_file[]': (filename, file)}
    ## 上传文件开始
    try:
      resp = requests.post(apiUrl, files = files)
    except:
      print('Something wrong when uploading file to {}'.format(self._domain))
      return None
    if (resp.status_code != 200):
      print('Upload status_code != 200')
      return None
    try:
      print('Upload respone is:\n{}'.format(resp.text))
      respJson = json.loads(resp.text)
      url = r'https://upload.cc/'+respJson['success_image'][0]['url']
    except:
      print('Upload respone is not json or have no ["success_image"][0]["url"]!'
        )
      return None
    return {'url': url, 'data': resp.text}
 
## 上传管理器类,是本模块的主要类,外部用这个.
class UploadManager:

  def __init__(self):
    ## 创建所有uploader的列表
    self.uploaders = [
      imgurl_org_Uploader(),
      sm_ms_Uploader(),
      upload_cc_Uploader()
    ]
    return
    
  ## 上传文件,指定域名.上传成功返回数据表,否则返回None
  def upload(self, domain, filepath, tags = []):
    ## 找到domain对应的上传器,找不到返回None
    uploader = None
    for i in range(len(self.uploaders)):
      if (self.uploaders[i]._domain == domain):
        uploader = self.uploaders[i]
    if (not uploader):
      print('No uploader {}'.format(domain))
      return None
    print('Got uploader for {} !'.format(domain))
    ## 上传并返回
    return uploader.upload(filepath, tags)

## 测试
if __name__ == '__main__':
  import sys
  
  um = UploadManager()

  domain = 'imgurl.org'
  #filepath = r'D:\MyDocument\MyPic\其他\铃鹿\suzuka_op_yange01.png'
  #filepath = r'D:\MyDocument\MyPic\其他\铃鹿\铃鹿_op_童年.png'
  filepath = r'D:\MyDocument\MyPic\壁纸图标\壁纸\海浪1.jpg'
  #filepath = r'D:\MyDocument\MyPic\BBS\爱美的要求.jpg'
  #filepath = r'D:\MyDocument\MyPic\其他\izumo3立绘集\ps自制izumo3三女主立绘.png'
  #filepath = r'D:\MyDocument\MyPic\BBS\aonatsu_yui_big.jpg'
  tags = ['壁纸', '测试']
  # 上传
  um.upload(domain, filepath, tags)